/*
 * @Author: 杰编程 2306248989@qq.com
 * @Date: 2023-07-29 20:07:01
 * @LastEditors: 杰编程 2306248989@qq.com
 * @LastEditTime: 2023-07-29 23:16:36
 * @FilePath: /lesson22/pthreadPool/ThreadPool_V1.hpp
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#pragma once

#include <iostream>
#include <vector>
#include <queue>
#include <unistd.h>
#include "thread.hpp"
#include "lockGuard.hpp"

const int N = 5;

template <class T>
class ThreadPool
{
private:
    pthread_mutex_t *getLock()
    {
        return &_mutex;
    }
    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }
    void threadWakeUp()
    {
        pthread_cond_signal(&_cond);
    }

    bool isEmpty()
    {
        return _tasks.empty();
    }

    T popTask()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

public:
    ThreadPool(int num = N) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    void init()
    {
        for (int i = 0; i < _num; ++i)
        {
            _threads.push_back(thread(i, threadRoutine, this));
        }
    }

    static void threadRoutine(void *args)
    {
        pthread_detach(pthread_self());
        ThreadPool<T> *t = static_cast<ThreadPool<T> *>(args);
        while (true)
        {
            T task;
            {
                LockGuard lockguard(t->getLock());
                while (t->isEmpty())
                {
                    t->threadWait();
                }
                task = t->popTask();
            }
            task();
            std::cout << "thread done, " << task.formatRes() << std::endl;
        }
    }

    void start()
    {
        for (auto &t : _threads)
        {
            t.run();
        }
    }

    void check()
    {
        for (auto &t : _threads)
        {
            std::cout << t.threadname() << " running..." << std::endl;
        }
    }
    void pushTask(const T &task)
    {
        LockGuard lockguard(&_mutex);
        _tasks.push(task);
        threadWakeUp();
    }

    ~ThreadPool()
    {
        for (auto &t : _threads)
        {
            t.join();
        }
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    std::vector<thread> _threads;
    int _num;
    std::queue<T> _tasks;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};